package top.ajunnihao.core.spring.aspect;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

/**
 * TODO: 日志记录 AOP，记录 Controller 方法日志
 *   记录日志内容:
 *   请求 url
 *   访问者 ip
 *   调用的方法名称 classMethod
 *   方法参数 args
 *   返回的内容
 *
 * @author LiJun
 * @version 1.0
 * @date 2020/3/12 12:31
 * @since jdk-1.8.0
 */
@Aspect
@Component
public class LogAspect {

    /**
     * 日志记录
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(LogAspect.class);

    /**
     * 切面:所有的 Controller 方法( controller 包及其子包下的所有类的所有方法)
     */
    @Pointcut("execution(* top.ajunnihao.core.web.controller..*.*(..))")
    public void logPc() {
    }

    public void doBefore(JoinPoint joinPoint) {
        // 获取请求对象
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        assert attributes != null;
        HttpServletRequest request = attributes.getRequest();

        // 请求 url
        String url = request.getRequestURL().toString();
        // 访问者 ip
        String ip = request.getRemoteAddr();
        // 调用的方法名称 classMethod
        Signature signature = joinPoint.getSignature();
        String classMethod = signature.getDeclaringTypeName() + '.' + signature.getName();
        // 方法参数 args
        Object[] args = joinPoint.getArgs();
        RequestLog requestLog = new RequestLog(url, ip, classMethod, args);

        // 日志记录
        LOGGER.info("Request : {}", requestLog);
    }

    @Around("logPc()")
    public Object doAround(ProceedingJoinPoint pjp) {
        Object retVal = null;
        try {
            doBefore(pjp);
            Object[] args = pjp.getArgs();
            retVal = pjp.proceed(args);
            if (retVal != null) {
                LOGGER.info("ReturnValue : {}, ReturnType : {}", retVal, retVal.getClass().getTypeName());
            } else {
                LOGGER.info("ReturnValue : {}, ReturnType : {}", Void.class, Void.class.getTypeName());
            }
        } catch (Throwable throwable) {
            LOGGER.info("Exception : " + throwable);
            throwable.printStackTrace();
        }
        return retVal;
    }

    /**
     * 日志信息对象
     */
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    private static class RequestLog {
        private String url;
        private String ip;
        private String classMethod;
        private Object[] args;
    }

}
